import datetime
import hashlib
from pathlib import Path

import qrcode
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

choices_unit = [('mL', 'mL'), ('L', 'L'), ('g', 'g'), ('kg', 'kg'), ('EA', 'EA')]
sales_channel_choices = [(1, '上海'), (2, '杭州')]
choices_material_in_state = [('1', '未到货'), ('2', '部分到货'), ('3', '已到货')]
choices_material_in_pay_state = [('1', '未付款'), ('2', '部分付款'), ('3', '已付款')]
choice_material_out_type = [
    ('product', '生产'), ('develop', '研发1'), ('develop2', '研发2'), ('damaged', '破损/丢弃'),
    ('lost', '盘损'), ('return', '退货'), ('sales', '转售'), ('others', '其他')]
choices_product_state = [
            ('plan', '计划排产'),
            ('brew', '糖化'),
            ('primary_fermentation', '主发酵'),
            ('secondary_fermentation', '后发酵'),
            ('cooling', '降温'),
            ('cold_storage', '冷藏'),
            ('filling', '可灌装'),
            ('product_in', '灌装完成'),
            ('complete', '入库完成'),
        ]
choices_tank_state = choices_product_state + [('need_wash', '待清洗'), ('washed', '已清洗'), ('broken', '待修')]
choices_process_type = [('UHT', '瞬杀'), ('centrifugal', '离心'), ('dry_hopping', '干投'), ('mix', '调配')]
process_type_related_state = {
    'UHT': 'filling',
    'centrifugal': 'filling',
    'dry_hopping': 'cooling',
    'mix': 'filling',
}
choices_sales_channel_type = [(1, '线下渠道'), (2, '线上渠道'), (3, '经销渠道'), (4, '成本')]
choices_department_type = [(1, '利润中心'), (2, '费用中心')]
category_type_choices = [(1, '原料'), (2, '辅料'), (3, '包材'), (4, '其他')]


def get_mos(filter_by=None):
    res = ''
    if filter_by:
        mos = MaterialOut.objects.filter(
            **filter_by
        ).values(
            'related_order__code',
            'material_batch__code',
            'material_batch__material__code',
            'material_batch__material__name',
            'material_batch__material__unit',
            'amount',
        )
        res += '<ul>'
        for mo in mos:
            res += '<li style="white-space: nowrap;">{0} [{1} {2} ({3}) {4} {5}]</li>'.format(
                mo['related_order__code'],
                mo['material_batch__material__code'],
                mo['material_batch__material__name'],
                mo['material_batch__code'],
                mo['amount'],
                mo['material_batch__material__unit'],
            )
        res += '</ul>'
    return res


class Company(models.Model):
    name = models.CharField(_('名称'), max_length=255, null=True, unique=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('公司')
        verbose_name_plural = _('公司')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class UserLevel(models.Model):
    level_code = models.SmallIntegerField(_('等级编号'), null=True, unique=True)
    level_name = models.CharField(_('等级名称'), max_length=100, null=True, unique=True)
    desc = models.TextField(_('等级描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('用户等级')
        verbose_name_plural = _('用户等级')

    def __str__(self):
        return "{0} {1}".format(
            self.level_code,
            self.level_name,
        )


class WxUser(AbstractUser):
    # 微信同步的用户信息
    openid = models.CharField(_('微信小程序OpenID'), max_length=100, unique=True, null=True, blank=True)
    openid_gzh = models.CharField(_('微信公众号OpenID'), max_length=100, unique=True, null=True, blank=True)
    avatar_url = models.URLField(_('头像'), null=True, blank=True)
    nick_name = models.CharField(_('昵称'), max_length=100, null=True, blank=True, unique=True)
    gender = models.SmallIntegerField(
        verbose_name=_('性别'), help_text=_('0-->未知, 1-->男, 2-->女'),
        choices=((1, '男'), (2, '女'), (0, '未知')), null=True, blank=True)
    language = models.CharField(_('语言'), max_length=100, null=True, blank=True)
    city = models.CharField(_('城市'), max_length=200, null=True, blank=True)
    province = models.CharField(_('省份'), max_length=200, null=True, blank=True)
    country = models.CharField(_('国家'), max_length=200, null=True, blank=True)
    # 附加信息
    full_name = models.CharField(_('真实姓名'), max_length=100, null=True, blank=True)
    date_of_birth = models.DateField(_('出生日期'), null=True, blank=True)
    desc = models.TextField(_('描述'), max_length=2000, null=True, blank=True)
    mobile = models.CharField(_('手机号'), max_length=100, null=True, blank=True)
    user_level = models.ForeignKey(
        UserLevel,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('用户等级')
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'
        ordering = ['-id']

    def __str__(self):
        if self.full_name:
            res = self.full_name
        else:
            res = self.username
        return "[{0}] {1}".format(
            self.pk,
            res,
        )


class Department(models.Model):
    name = models.CharField(_('名称'), max_length=255, null=True)
    desc = models.TextField(_('描述'), max_length=2000, null=True, blank=True)
    department_type = models.SmallIntegerField(_('部门类型'), null=True, blank=True, choices=choices_department_type)
    order_index = models.SmallIntegerField(_('排序编号'), null=True, default=1)
    is_active = models.BooleanField(_('在用'), default=True)

    objects = models.Manager()

    class Meta:
        ordering = ['order_index']
        verbose_name = _('部门')
        verbose_name_plural = _('部门')

    def __str__(self):
        return "{0}".format(
            self.name
        )


class Staff(models.Model):
    code = models.IntegerField(_('工号'), null=True, blank=True, unique=True)
    last_name = models.CharField(_('姓'), max_length=100, null=True)
    first_name = models.CharField(_('名'), max_length=100, null=True)
    full_name = models.CharField(_('全名'), max_length=100, null=True, blank=True)
    english_name = models.CharField(_('英文名'), max_length=100, null=True, blank=True)
    gender = models.PositiveSmallIntegerField(
        _('性别'), default=0, choices=[(0, '未知'), (1, '男'), (2, '女')], null=True)
    position = models.CharField(_('职务'), max_length=100, null=True, blank=True)
    related_department = models.ForeignKey(
        Department,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        limit_choices_to={'is_active': True},
        verbose_name=_('部门')
    )
    company = models.ForeignKey(
        Company,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('所属公司')
    )
    desc = models.TextField(_('描述'), max_length=2000, null=True, blank=True)
    date_of_birth = models.DateField(_('生日'), null=True, blank=True)
    date_of_entry = models.DateField(_('入职日期'), null=True, blank=True)
    date_of_quit = models.DateField(_('离职日期'), null=True, blank=True)
    mobile = models.CharField(_('手机号'), max_length=100, null=True, blank=True)
    e_mail = models.CharField(_('邮箱'), max_length=100, null=True, blank=True)
    is_active = models.BooleanField(_('在职'), default=True)
    related_user = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('关联用户'),
        limit_choices_to={'is_active': True},
        related_name='staff_related_user',
    )
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='staff_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='staff_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['code']
        verbose_name = _('员工')
        verbose_name_plural = _('员工')

    def __str__(self):
        return "{0}{1}".format(
            self.last_name,
            self.first_name,
        )


class CustomerLevel(models.Model):
    level_code = models.SmallIntegerField(_('等级编号'), null=True, unique=True)
    level_name = models.CharField(_('等级名称'), max_length=100, null=True, unique=True)
    desc = models.TextField(_('等级描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('客户等级')
        verbose_name_plural = _('客户等级')

    def __str__(self):
        return "{0} {1}".format(
            self.level_code,
            self.level_name,
        )


class Customer(models.Model):
    nickname = models.CharField(_('简称'), max_length=255, null=True, unique=True)
    full_name = models.CharField(_('全称'), max_length=255, null=True, blank=True)
    contact = models.CharField(_('联系人姓名'), max_length=50, null=True, blank=True)
    contact_mobile = models.CharField(_('联系人手机号'), max_length=50, null=True, blank=True)
    need_statistic = models.BooleanField(_('需要统计'), default=True)
    customer_level = models.ForeignKey(
        CustomerLevel,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('客户类别')
    )
    department = models.ForeignKey(
        Department,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name=_('部门')
    )
    related_salesman = models.ForeignKey(
        Staff,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        limit_choices_to={'is_active': True},
        verbose_name=_('关联销售员')
    )
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='customer_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='customer_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('客户')
        verbose_name_plural = _('客户')

    def __str__(self):
        return "{0} {1}".format(
            self.nickname,
            self.customer_level,
        )


class CustomerUser(models.Model):
    customer = models.ForeignKey(
        Customer,
        on_delete=models.CASCADE,
    )
    user = models.OneToOneField(
        WxUser,
        help_text=_('一个用户只能够一个客户关联'),
        on_delete=models.CASCADE,
    )

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('客户的用户')
        verbose_name_plural = _('客户的用户')

    def __str__(self):
        return "{0}".format(
            self.user,
        )


class Equipment(models.Model):
    code = models.CharField(_('编码'), max_length=255, null=True, unique=True)
    name = models.CharField(_('名称'), max_length=255, null=True)
    model = models.CharField(_('型号'), max_length=255, null=True, blank=True)
    price = models.DecimalField(_('设备价格'), max_digits=10, decimal_places=2, null=True, blank=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    design_capacity = models.CharField(_('设计产能'), max_length=200, null=True, blank=True)
    actual_capacity = models.CharField(_('实际产能'), max_length=200, null=True, blank=True)
    problems = models.CharField(_('存在问题'), max_length=200, null=True, blank=True)
    problem_solutions = models.CharField(_('问题解决'), max_length=200, null=True, blank=True)
    is_tank = models.BooleanField(_('是容器'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='equipment_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='equipment_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['code']
        verbose_name = _('设备')
        verbose_name_plural = _('设备')

    def __str__(self):
        return "{0}".format(
            self.code,
        )


class Warehouse(models.Model):
    warehouse_type = models.CharField(
        _('仓库类型'), max_length=100,
        default='material', choices=[('material', '原料仓库'), ('product', '产成品仓库')])
    name = models.CharField(_('名称'), max_length=255, null=True, unique=True)
    address = models.CharField(_('地址'), max_length=255, null=True, blank=True)
    contact = models.CharField(_('联系人姓名'), max_length=50, null=True, blank=True)
    contact_mobile = models.CharField(_('联系人手机号'), max_length=50, null=True, blank=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    is_active = models.BooleanField(_('在用'), default=True)
    is_customer = models.BooleanField(_('客户仓库'), default=False)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('仓库')
        verbose_name_plural = _('仓库')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class MaterialCategory(models.Model):
    name = models.CharField(_('归类名称'), max_length=255, null=True, unique=True)
    name_en = models.CharField(_('英文名称'), max_length=255, null=True, blank=True, unique=True)
    category_type = models.SmallIntegerField(_('类型'), null=True, choices=category_type_choices)
    is_packing = models.BooleanField(_('是包材'), default=False)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('原料归类')
        verbose_name_plural = _('原料归类')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class Supplier(models.Model):
    name = models.CharField(_('供应商简称'), max_length=255, null=True, unique=True)
    contact = models.CharField(_('联系人姓名'), max_length=50, null=True, blank=True)
    contact_mobile = models.CharField(_('联系人手机号'), max_length=50, null=True, blank=True)
    company_name = models.CharField(_('公司名称'), max_length=255, null=True, blank=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='supplier_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='supplier_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('供应商')
        verbose_name_plural = _('供应商')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class Material(models.Model):
    code = models.CharField(_('物料代码'), max_length=255, null=True, blank=True, unique=True)
    name = models.CharField(_('名称'), max_length=255, null=True)
    name_en = models.CharField(_('英文名称'), max_length=255, null=True, blank=True)
    material_category = models.ForeignKey(
        MaterialCategory,
        on_delete=models.PROTECT,
        verbose_name=_('归类')
    )
    total_in = models.DecimalField(_('入库总量'), null=True, blank=True, max_digits=12, decimal_places=4)
    left_amount = models.DecimalField(_('剩余数量'), null=True, blank=True, max_digits=12, decimal_places=4)
    unit = models.CharField(_('计量单位'), max_length=10, null=True, blank=True, choices=choices_unit)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    supplier = models.ManyToManyField(
        Supplier,
        blank=True,
        limit_choices_to={'is_active': True},
        verbose_name=_('供应商')
    )
    is_active = models.BooleanField(_('在用'), default=True)
    is_public = models.BooleanField(_('公开'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('原料')
        verbose_name_plural = _('原料')

    def __str__(self):
        return "{0} {1} 剩余：{2} {3}".format(
            self.code,
            self.name,
            self.left_amount if self.left_amount else 0,
            self.unit if self.unit else 0,
        )

    @mark_safe
    def batches_property(self):
        mbs = MaterialBatch.objects.filter(material_id=self.pk).values(
            'code', 'total_in', 'left_amount', 'unit', 'unit_cost', 'expiration_date'
        ).order_by('expiration_date')
        res = '<ul>'
        for mb in mbs:
            color = 'black'
            if mb['code']:
                color = 'black'
            if mb['expiration_date']:
                expiration_date = mb['expiration_date']
            else:
                expiration_date = ''
            res += '<li style="white-space: nowrap;color:{0}">{1}</li>'.format(
                color,
                '{0} [入库: {2}{1} 剩余: {3}{1}] 单价: {5}元 {4} </li>'.format(
                    mb['code'],
                    mb['unit'],
                    mb['total_in'],
                    mb['left_amount'],
                    expiration_date,
                    mb['unit_cost']
                ))
        res += '</ul>'
        return res

    batches_property.short_description = "批次详情"
    batches_items = property(batches_property)


class PackageUnit(models.Model):
    display = models.CharField(_('显示名称'), max_length=100, null=True, unique=True)
    amount = models.DecimalField(_('数量'), max_digits=10, decimal_places=2)
    unit = models.CharField(_('单位'), max_length=10, null=True, choices=choices_unit)
    is_material = models.BooleanField(_('是原料单位'), default=True)
    is_product = models.BooleanField(_('是产品单位'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='package_unit_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='package_unit_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('包装单位')
        verbose_name_plural = _('包装单位')

    def __str__(self):
        return "{0}".format(
            self.display
        )


class MaterialBatch(models.Model):
    code = models.CharField(_('批次号'), max_length=100, default='unknown')
    material = models.ForeignKey(
        Material,
        on_delete=models.CASCADE,
        verbose_name=_('原料名称'),
    )
    package_unit = models.ForeignKey(
        PackageUnit,
        limit_choices_to={'is_material': True},
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('包装单位')
    )
    unit = models.CharField(_('计量单位'), max_length=10, null=True, choices=choices_unit)
    total_in = models.DecimalField(_('入库总量'), null=True, blank=True, max_digits=12, decimal_places=4)
    left_amount = models.DecimalField(_('剩余数量'), null=True, blank=True, max_digits=12, decimal_places=4)
    unit_price = models.DecimalField(_('采购单价（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit_cost = models.DecimalField(_('单位成本（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    # 单位成本 = 单位采购价格 + 单位运输成本
    production_date = models.DateField(_('生产日期'), null=True, blank=True)
    shelf_life = models.IntegerField(
        _('保质期/天'), default=365, help_text='一年=365, 二年=730, 三年=1095，四年=1461', null=True)
    expiration_date = models.DateField(_('到期日期'), null=True, blank=True)
    notes = models.TextField(_('备注'), max_length=1000, null=True, blank=True)
    report = models.FileField(_('质检报告'), null=True, blank=True, upload_to='material/%Y/%m/%d/')
    report_is_upload = models.BooleanField(_('质检单已上传'), default=False)
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_batch_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_batch_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['expiration_date', '-id']
        unique_together = ['code', 'material']
        verbose_name = _('原料批次')
        verbose_name_plural = _('原料批次')

    def __str__(self):
        return "{0} {1} {2}({3},{6}) [剩余：{4} {5}]".format(
            self.production_date,
            self.material.code,
            self.material.name,
            self.package_unit,
            self.left_amount if self.left_amount else 0,
            self.unit if self.unit else 'EA',
            self.code
        )


class MaterialPurchaseOrder(models.Model):
    code = models.CharField(_('采购订单号'), null=True, blank=True, max_length=100, unique=True)
    supplier = models.ForeignKey(
        Supplier,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('供应商')
    )
    order_date = models.DateField(_('下单日期'), null=True)
    exp_record_date = models.DateField(_('预计到货日期'), null=True, blank=True)
    state = models.CharField(
        _('到货状态'), null=True, choices=choices_material_in_state, max_length=100, default='1')
    state_pay = models.CharField(
        _('支付状态'), null=True, choices=choices_material_in_pay_state, max_length=100, default='1')
    total_price = models.DecimalField(_('商品总价（元）'), max_digits=10, decimal_places=2, null=True, blank=True)
    carriage_cost = models.DecimalField(_('总运输费（元）'), max_digits=10, decimal_places=2, null=True, default=0)
    total_discount = models.DecimalField(_('总折扣（元）'), max_digits=10, decimal_places=2, null=True, default=0)
    total_cost = models.DecimalField(_('总应付款（元）'), max_digits=10, decimal_places=2, null=True, blank=True)
    total_payed = models.DecimalField(_('总已付款（元）'), max_digits=10, decimal_places=2, null=True, blank=True)
    file = models.FileField(_('附件'), null=True, blank=True, upload_to='material/%Y/%m/%d/')
    file_extra = models.FileField(_('附件2'), null=True, blank=True, upload_to='material/%Y/%m/%d/')
    # is_active = models.BooleanField(_('未完全到货'), default=True)
    applied_by = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('申请人'),
        related_name='material_purchase_order_applied_by',
    )
    confirmed_by = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('审核人'),
        related_name='material_purchase_order_confirmed_by',
    )
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    notes = models.CharField(_('备注'), max_length=255, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_purchase_order_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_purchase_order_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-order_date']
        verbose_name = _('原材料采购申请单')
        verbose_name_plural = _('原材料采购申请单')

    def __str__(self):
        return "{0} {1} {2} {3}".format(
            self.code, self.order_date, self.supplier,
            self.notes if self.notes else ''
        )

    @mark_safe
    def material_items_property(self):
        # 采购明细
        mis = MaterialPurchaseOrderItem.objects.filter(related_purchase_order_id=self.pk).values(
            'material__code', 'material__name', 'amount', 'unit_price', 'package_unit__unit', 'price', 'amount_in',
            'is_arrived'
        )
        res = '<ul>'
        for mi in mis:
            amount_in = mi['amount_in'] if mi['amount_in'] else 0
            color = 'red'
            if mi['is_arrived']:
                color = 'black'
            res += '<li style="white-space: nowrap;color:{0}">{1}</li>'.format(
                color,
                '{0} {1} ({2}{4}*{3}元/{4}={5}元)[采购: {2}{4} 入库: {6}{4}]'.format(
                    mi['material__code'],
                    mi['material__name'],
                    mi['amount'],
                    mi['unit_price'],
                    mi['package_unit__unit'],
                    mi['price'],
                    amount_in)
            )
        res += '</ul>'
        return res

    material_items_property.short_description = "采购明细"
    material_items = property(material_items_property)


class MaterialPurchaseOrderItem(models.Model):
    related_purchase_order = models.ForeignKey(
        MaterialPurchaseOrder,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('采购申请单')
    )
    material = models.ForeignKey(
        Material,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('原料')
    )
    package_unit = models.ForeignKey(
        PackageUnit,
        limit_choices_to={'is_material': True},
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('包装单位')
    )
    packs = models.IntegerField(_('数量(包装)'), null=True)
    amount = models.DecimalField(_('采购数量'), max_digits=12, decimal_places=4, null=True, blank=True)
    amount_in = models.DecimalField(_('入库数量'), max_digits=12, decimal_places=4, null=True, default=0)
    unit_price = models.DecimalField(_('采购单价（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit_cost = models.DecimalField(_('单位成本（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    price = models.DecimalField(_('小计-金额（元）'), max_digits=10, decimal_places=2, null=True)
    exp_record_date = models.DateField(_('要求到货日期'), null=True, blank=True)
    is_arrived = models.BooleanField(_('已到货'), default=False)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('采购申请单明细')
        verbose_name_plural = _('采购申请单明细')
        unique_together = ['related_purchase_order', 'material']

    def __str__(self):
        return "{0} {1} {2} ￥{3}".format(
            self.material.name, self.amount, self.material.unit, self.price
        )


class MaterialInOrder(models.Model):
    code = models.CharField(_('原材料入库单号'), null=True, blank=True, max_length=100, unique=True)
    related_purchase_order = models.ForeignKey(
        MaterialPurchaseOrder,
        on_delete=models.SET_NULL,
        limit_choices_to={'is_confirmed': False},
        null=True,
        verbose_name=_('采购订单')
    )
    record_date = models.DateField(_('入库日期'), null=True)
    record_datetime = models.DateTimeField(_('入库时间'), null=True, blank=True, help_text=_('此时间为虚拟计算值'))
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('入库人员'),
        related_name='material_in_order_operator',
        limit_choices_to={'is_active': True}
    )
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_in_order_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_in_order_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    @mark_safe
    def material_items_property(self):
        res = ''
        if self.related_purchase_order:
            mis = MaterialPurchaseOrderItem.objects.filter(
                related_purchase_order_id=self.related_purchase_order.pk
            ).values(
                'material__code', 'material__name', 'amount', 'package_unit__unit', 'is_arrived', 'amount_in'
            )
            if mis:
                res += '<ul>'
                for mi in mis:
                    if mi:
                        color = 'red'
                        if mi['is_arrived'] is True:
                            color = 'black'
                        res += '<li style="white-space: nowrap;color:{5}">{0} {1} [采购: {2}{3} 入库: {4}{3}]</li>'.format(
                            mi['material__code'],
                            mi['material__name'],
                            mi['amount'],
                            mi['package_unit__unit'],
                            mi['amount_in'],
                            color
                        )
                res += '</ul>'
        return res

    material_items_property.short_description = "采购明细"
    material_items = property(material_items_property)

    @mark_safe
    def material_ins_property(self):
        mis = MaterialIn.objects.filter(related_order_id=self.pk).values(
            'material_batch__code', 'material_batch__material__code', 'material_batch__material__name',
            'amount', 'unit'
        )
        res = '<ul>'
        for mi in mis:
            res += '<li style="white-space: nowrap;">{0} {1} ({2}) +{3} {4}</li>'.format(
                mi['material_batch__material__code'],
                mi['material_batch__material__name'],
                mi['material_batch__code'],
                mi['amount'], mi['unit']
            )
        res += '</ul>'
        return res

    material_ins_property.short_description = "入库明细"
    material_ins = property(material_ins_property)

    class Meta:
        ordering = ['-record_date']
        verbose_name = _('原材料入库单')
        verbose_name_plural = _('原材料入库单')

    def __str__(self):
        return "{0} {1}".format(
            self.code, self.record_date,
        )


class MaterialIn(models.Model):
    # 原料入库明细
    related_order = models.ForeignKey(
        MaterialInOrder,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('原材料入库单')
    )
    material_batch = models.ForeignKey(
        MaterialBatch,
        on_delete=models.PROTECT,
        null=True,
        verbose_name=_('原料批次')
    )
    material = models.ForeignKey(
        Material,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name=_('原料')
    )
    related_purchase_detail = models.ForeignKey(
        MaterialPurchaseOrderItem,
        related_name='related_material_in',
        related_query_name='related_material_in',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('关联采购记录')
    )
    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        null=True,
        limit_choices_to={'is_active': True, 'warehouse_type': 'material'},
        verbose_name=_('仓库')
    )
    packs = models.DecimalField(_('数量(包装)'), max_digits=12, decimal_places=4, null=True, blank=True)
    amount = models.DecimalField(_('小计-数量'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit = models.CharField(_('单位'), max_length=10, null=True, choices=choices_unit)
    unit_price = models.DecimalField(_('采购单价（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit_cost = models.DecimalField(_('单位成本（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    qualified = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('质检人员'),
        related_name='material_in_qualified',
        limit_choices_to={'is_active': True}
    )
    is_checked = models.BooleanField(_('已质检'), default=False)
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_in_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_in_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True, db_index=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('原料入库')
        verbose_name_plural = _('原料入库')
        unique_together = ['related_order', 'material_batch']

    def __str__(self):
        return "{0}".format(
            self.material_batch
        )


class ProductCategory(models.Model):
    name = models.CharField(_('名称'), max_length=255, null=True, unique=True)
    name_en = models.CharField(_('英文名称'), max_length=255, null=True, blank=True, unique=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('产品归类')
        verbose_name_plural = _('产品归类')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class ProductName(models.Model):
    code = models.CharField(_('产品编号'), max_length=100, unique=True)
    name = models.CharField(_('产品名称'), max_length=255, unique=True)
    name_en = models.CharField(_('产品英文名称'), max_length=255, unique=True, null=True, blank=True)
    product_category = models.ForeignKey(
        ProductCategory,
        on_delete=models.PROTECT,
        null=True,
        verbose_name=_('品牌/分类')
    )
    abv = models.DecimalField(_('酒精度'), null=True, blank=True, decimal_places=2, max_digits=5)
    plato = models.DecimalField(_('麦汁度'), null=True, blank=True, decimal_places=1, max_digits=5)
    ibu = models.DecimalField(_('IBU'), null=True, blank=True, decimal_places=1, max_digits=5)
    color = models.CharField(_('颜色'), null=True, blank=True, max_length=255)
    materials = models.CharField(_('原料'), null=True, blank=True, max_length=225)
    standard = models.CharField(_('执行标准'), null=True, blank=True, max_length=225)
    is_oem = models.BooleanField(_('可ODM产品'), default=False, help_text=_('TR的配方和产品，可以被用于ODM生产'))
    is_active = models.BooleanField(_('在用'), default=True)
    related_name = models.ForeignKey(
        'self',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('关联名称')
    )
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='product_name_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='product_name_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    @property
    def packages(self):
        pps = PackageGroup.objects.filter(
            packageitem__product_package__product_name=self.pk,
            is_active=True
        ).values('name', 'barcode', 'current_stock')
        return pps

    @property
    def brand(self):
        res = None
        if self.product_category:
            res = getattr(self, 'product_category').name
        return res

    class Meta:
        ordering = ['id']
        verbose_name = _('产品名称')
        verbose_name_plural = _('产品名称')

    def __str__(self):
        return "[{0}]-{1}".format(self.code, self.name)


class Award(models.Model):
    name = models.CharField(_('名称'), max_length=255, null=True)
    award_date = models.DateField(_('获奖日期'), null=True, blank=True)
    award_level = models.PositiveSmallIntegerField(_('获奖等级'), null=True, default=1)
    product_name = models.ForeignKey(
        ProductName,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('产品名称')
    )
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-award_date', 'award_level']
        verbose_name = _('获奖记录')
        verbose_name_plural = _('获奖记录')

    def __str__(self):
        return "{0} {1}".format(
            self.name, self.product_name
        )


class ProcessingCost(models.Model):
    name = models.CharField(_('名称'), max_length=255, null=True, unique=True)
    cost = models.DecimalField(_('元/千升'), null=True, decimal_places=2, max_digits=10)
    is_active = models.BooleanField(_('可用'), default=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='processing_cost_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='processing_cost_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['pk']
        verbose_name = _('加工费用')
        verbose_name_plural = _('加工费用')

    def __str__(self):
        return "{0}元/千升 {1}".format(
            self.cost, self.name
        )


class ProductBatch(models.Model):
    code = models.CharField(_('批次号'), max_length=100, unique=True)
    product_state = models.CharField(
        _('批次状态'),
        max_length=100,
        default='plan',
        choices=choices_product_state
    )
    product_name = models.ForeignKey(
        ProductName,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('产品名称')
    )
    current_tank = models.ForeignKey(
        Equipment,
        on_delete=models.SET_NULL,
        null=True,
        limit_choices_to={'is_tank': True},
        verbose_name=_('当前容器'),
        help_text=_('批次当前所在的发酵罐')
    )
    processing_cost = models.ForeignKey(
        ProcessingCost,
        on_delete=models.SET_NULL,
        limit_choices_to={'is_active': True},
        null=True,
        blank=True,
        verbose_name=_('加工费用')
    )
    start_date = models.DateField(_('起始日期'), null=True, help_text=_('麦汁入罐的日期'))
    finish_date = models.DateField(_('截止日期'), null=True, blank=True, help_text=_('灌装完成的日期'))
    total_volume = models.IntegerField(_('批次容量/L'), null=True, blank=True, default=2000)
    est_volume = models.IntegerField(_('预估出酒量/L'), null=True, blank=True, default=2000)
    act_volume = models.DecimalField(
        _('实际出酒量/L'), null=True, blank=True, default=0, max_digits=10, decimal_places=2)
    product_yield = models.DecimalField(_('出酒率'), max_digits=5, decimal_places=2, null=True, blank=True)
    report = models.FileField(_('生产工单'), null=True, blank=True, upload_to='product/%Y/%m/%d/')
    is_active = models.BooleanField(_('在生产中'), default=True)
    need_statistic = models.BooleanField(_('需要统计'), default=True)
    qr_code = models.FileField(_('QR图'), null=True, blank=True, upload_to='product_batch/qr_code/')
    notes = models.TextField(_('备注'), max_length=1000, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='product_batch_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='product_batch_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    def delta_days_property(self):
        delta_days = None
        if self.start_date:
            delta_days = (datetime.datetime.now().date() - getattr(self, 'start_date')).days
        return delta_days

    delta_days_property.short_description = "距今天数"
    delta_days = property(delta_days_property)

    def calculate_act_volume(self, date_before=None):
        # 计算实际出酒量
        act_volume = 0
        pbs = PackBatch.objects.filter(product_batch_id=self.pk)
        if date_before:
            pbs = pbs.filter(pack_start__lte=date_before)
        for pb in pbs:
            amount = pb.pack_amount
            package_unit = pb.package_unit
            if package_unit:
                unit = package_unit.unit
                pack_unit_amount = package_unit.amount
                if unit == 'mL':
                    act_volume += float(amount) * float(pack_unit_amount) * 0.001
                else:
                    act_volume += float(amount) * float(pack_unit_amount)
        return round(act_volume, 2)

    @mark_safe
    def operations_property(self):
        res = ''
        bbs = BrewBatch.objects.filter(product_batch_id=self.pk).values(
            'pk', 'code', 'brew_start', 'brew_end', 'operator__full_name', 'file_is_upload', 'is_confirmed',
            'report'
        ).order_by('pk')
        if bbs:
            res += '<h3>糖化记录</h3>'
            res += '<ul>'
            for bb in bbs:
                res += '<li style="white-space: nowrap;">{0} {1} {2} {3}</li>'.format(
                    '<a href="{}" target="_blank">{}</a>'.format(
                        '/ht/oa/brewbatch/{}/'.format(bb['pk']),
                        bb['code'],
                    ),
                    bb['brew_start'],
                    bb['operator__full_name'],
                    '<a href="/media/{}" target="_blank">工艺单</a>'.format(bb['report']) if bb['report'] else ''
                )
            res += '</ul>'
        prs = ProcessRecord.objects.filter(product_batch_id=self.pk).values(
            'pk', 'process_type', 'operator__full_name', 'process_start', 'code', 'file'
        ).order_by('pk')
        if prs:
            res += '<h3>操作记录</h3>'
            res += '<ul>'
            for bb in prs:
                process_type = bb['process_type']
                process_type_show = ''
                for pt in choices_process_type:
                    if pt[0] == process_type:
                        process_type_show = pt[1]
                res += '<li style="white-space: nowrap;">{0} {1} {2} {3} {4}</li>'.format(
                    '<a href="{}" target="_blank">{}</a>'.format(
                        '/ht/oa/processrecord/{}/'.format(bb['pk']),
                        bb['code'],
                    ),
                    process_type_show,
                    bb['process_start'],
                    bb['operator__full_name'],
                    '<a href="/media/{}" target="_blank">工艺单</a>'.format(bb['file']) if bb['file'] else ''
                )
            res += '</ul>'
        pbs = PackBatch.objects.filter(product_batch_id=self.pk).values(
            'pk', 'code', 'package_unit__display', 'pack_amount', 'pack_start', 'operator__full_name', 'report'
        ).order_by('pk')
        if pbs:
            res += '<h3>灌装记录</h3>'
            res += '<ul>'
            for bb in pbs:
                res += '<li style="white-space: nowrap;">{0} ({1}*{2}) {3} {4}</li>'.format(
                    '<a href="{}" target="_blank">{}</a>'.format(
                        '/ht/oa/packbatch/{}/'.format(bb['pk']),
                        bb['code'],
                    ),
                    bb['package_unit__display'],
                    bb['pack_amount'],
                    bb['pack_start'],
                    '<a href="/media/{}" target="_blank">工艺单</a>'.format(bb['report'])if bb['report'] else ''
                )
            res += '</ul>'
        res += "<a target='_blank' href='/page/product_batch_cost/{}/'>" \
               "<label class='el-button'>生产成本</label></a>".format(self.pk)
        res += "<a target='_blank' href='/page/product_batch_detail/{}/'>" \
               "<label class='el-button'>详情页面</label></a>".format(self.pk)
        if self.qr_code:
            res += "<a target='_blank' href='{}'>" \
                   "<label class='el-button'>QR code</label></a>".format(getattr(self, 'qr_code').url)
        return res

    operations_property.short_description = "操作记录"
    operations = property(operations_property)

    @property
    def qr_key(self):
        m = hashlib.md5()
        m.update('{}-{}-{}'.format(self.pk, self.code, self.datetime_created).encode())
        psw = m.hexdigest()
        return psw

    def make_qr_code(self):
        url = '{}/page/product_batch_detail/{}/?key={}'.format(
            settings.DEFAULT_HOST, self.pk, self.qr_key
        )
        img_name = r'{}.png'.format(self.code)
        img_path = r'{}product_batch/qr_code/'.format(settings.MEDIA_ROOT)
        if not Path(img_path).exists():
            Path(img_path).mkdir(parents=True)
        img_file = r'{}{}'.format(img_path, img_name)
        qr = qrcode.QRCode(
            version=1,  #
            error_correction=qrcode.constants.ERROR_CORRECT_H,
            box_size=10,
            border=4
        )
        qr.add_data(url)
        qr.make(fit=True)
        img = qr.make_image()
        img.save(img_file)
        if Path(img_file).exists():
            self.qr_code = img_file.replace(settings.MEDIA_ROOT, '')
            self.save()

    def tank_info_property(self):
        tanks = EquipmentCurrentProductBatch.objects.filter(product_batch_id=self.pk)
        if tanks.count() > 1:
            tank_code_list = []
            tank_codes = tanks.values('equipment__code')
            for tank_code in tank_codes:
                tank_code_list.append(tank_code['equipment__code'])
            tank_info = ', '.join(tank_code_list)
        else:
            tank_info = self.current_tank.code
        return tank_info

    tank_info_property.short_description = "发酵罐信息"
    tank_info = property(tank_info_property)

    def total_processing_costs_property(self):
        total_processing_costs = 0
        if self.processing_cost:
            per_kl_processing_cost = getattr(self, 'processing_cost').cost
            if per_kl_processing_cost:
                per_kl_processing_cost = float(per_kl_processing_cost)
            else:
                per_kl_processing_cost = 0
            if self.total_volume:
                total_processing_costs = float(self.total_volume)*per_kl_processing_cost*0.001
        return total_processing_costs

    total_processing_costs_property.short_description = "总加工费"
    total_processing_costs = property(total_processing_costs_property)

    class Meta:
        ordering = ['-start_date']
        verbose_name = _('生产批次')
        verbose_name_plural = _('生产批次')

    def __str__(self):
        return "{0} {1} [{2}]".format(
            self.code, self.product_name, self.tank_info
        )


class ProductPackage(models.Model):
    code = models.CharField(_('产品编号'), max_length=100, null=True, blank=True, unique=True)
    name = models.CharField(_('产品包装名称'), max_length=255, null=True, unique=True)
    product_name = models.ForeignKey(
        ProductName,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name=_('产品名称')
    )
    package_unit = models.ForeignKey(
        PackageUnit,
        null=True,
        limit_choices_to={'is_product': True},
        on_delete=models.SET_NULL,
        verbose_name=_('包装规格')
    )
    licence = models.CharField(_('生产许可证'), null=True, blank=True, max_length=225)
    label_info = models.TextField(_('标签信息'), null=True, blank=True, max_length=2000)
    barcode = models.CharField(_('条码'), max_length=100, null=True, blank=True)
    shelf_life = models.IntegerField(_('保质期/天'), default=365)
    safety_stock = models.BigIntegerField(_('安全库存'), null=True, default=0)
    current_stock = models.BigIntegerField(_('当前库存'), null=True, default=0)
    width = models.IntegerField(_('直径/mm'), null=True, blank=True)
    height = models.IntegerField(_('高度/mm'), null=True, blank=True)
    weight = models.DecimalField(_('毛重/kg'), null=True, blank=True, decimal_places=3, max_digits=10)
    weight_net = models.DecimalField(_('净重/kg'), null=True, blank=True, decimal_places=3, max_digits=10)
    is_draft = models.BooleanField(_('是扎啤'), default=False)
    reference_cost = models.DecimalField(_('生产成本'), null=True, blank=True, decimal_places=2, max_digits=10)
    sales_cost = models.DecimalField(_('出厂成本'), null=True, blank=True, decimal_places=2, max_digits=10)
    has_cost = models.BooleanField(_('有生产成本'), default=False)
    has_sales_cost = models.BooleanField(_('有出厂成本'), default=False)
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='product_package_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='product_package_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        unique_together = ['product_name', 'package_unit']
        verbose_name = _('产品包装')
        verbose_name_plural = _('产品包装')

    def __str__(self):
        return "[{0}]-{1} {2}".format(
            self.code,
            self.product_name.name,
            self.package_unit
        )


class ProductPackageAttach(models.Model):
    product_package = models.ForeignKey(
        ProductPackage,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('产品包装')
    )
    file_name = models.CharField(_('附件名称'), max_length=255, null=True, blank=True)
    file = models.FileField(_('文件'), null=True, upload_to='product_pack/%Y/%m/%d/')
    is_active = models.BooleanField(_('在用'), default=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('产品包装-附件')
        verbose_name_plural = _('产品包装-附件')

    def __str__(self):
        return "{0}".format(
            self.file_name
        )

    def save(self, **kwargs):
        if not self.file_name:
            self.file_name = getattr(self, 'file').name
        super(ProductPackageAttach, self).save(**kwargs)


class Label(models.Model):
    product_package = models.ForeignKey(
        ProductPackage,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('产品包装')
    )
    label_name = models.CharField(_('标签名称'), null=True, max_length=100)
    label_image = models.ImageField(_('标签图片'), null=True, blank=True, upload_to='label/%Y/%m/%d/')
    label_file = models.FileField(_('标签图片'), null=True, blank=True, upload_to='label/%Y/%m/%d/')
    is_active = models.BooleanField(_('在用'), default=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('产品标签')
        verbose_name_plural = _('产品标签')

    def __str__(self):
        return "{0} {1}".format(
            self.label_name, self.label_image
        )


class ProductImage(models.Model):
    product_package = models.ForeignKey(
        ProductPackage,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('产品包装')
    )
    name = models.CharField(_('图片名称'), null=True, max_length=100, help_text=_('主图片命名为main'), default='main')
    image = models.ImageField(_('图片'), null=True, upload_to='label/%Y/%m/%d/')
    is_active = models.BooleanField(_('在用'), default=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('产品图片')
        unique_together = ['product_package', 'name']
        verbose_name_plural = _('产品图片')

    def __str__(self):
        return "{0} {1}".format(
            self.name, self.image
        )


class BrewBatch(models.Model):
    code = models.CharField(_('糖化批次号'), max_length=100, unique=True)
    product_batch = models.ForeignKey(
        ProductBatch,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name=_('酿造批次')
    )
    brew_start = models.DateTimeField(_('糖化开始'), null=True, blank=True)
    brew_end = models.DateTimeField(_('糖化结束'), null=True, blank=True)
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        help_text=_('不用填，可自动获取'),
        null=True,
        blank=True,
        verbose_name=_('操作负责人'),
        related_name='brew_batch_operator',
    )
    report = models.FileField(_('糖化工艺单'), null=True, blank=True, upload_to='brew/%Y/%m/%d/')
    file_is_upload = models.BooleanField(_('工单已上传'), default=False)
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='brew_batch_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='brew_batch_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('糖化批次')
        verbose_name_plural = _('糖化批次')

    def __str__(self):
        return "{0} [{1}]".format(
            self.code,
            self.product_batch
        )

    @mark_safe
    def materials(self):
        res = get_mos(filter_by={'related_order__brew_batch_id': self.pk})
        return res

    materials.short_description = "原料明细"
    operations = property(materials)


class PackBatch(models.Model):
    code = models.CharField(_('灌装批次号'), max_length=100, unique=True)
    product_batch = models.ForeignKey(
        ProductBatch,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name=_('生产批次'),
        related_name='pack_batch'
    )
    package_unit = models.ForeignKey(
        PackageUnit,
        null=True,
        on_delete=models.SET_NULL,
        limit_choices_to={'is_product': True},
        verbose_name=_('包装规格')
    )
    product_package = models.ForeignKey(
        ProductPackage,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('产品包装'),
        help_text=_('可不填，自动获取')
    )
    product_name = models.ForeignKey(
        ProductName,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('产品名称')
    )
    pack_start = models.DateTimeField(_('灌装开始'), null=True, blank=True)
    pack_end = models.DateTimeField(_('灌装结束'), null=True, blank=True)
    last_batch = models.BooleanField(_('最后一次灌装'), default=False)
    pack_amount = models.IntegerField(_('灌装数量'), null=True, default=0)
    left_amount = models.IntegerField(_('剩余数量'), null=True, default=0)
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        help_text=_('不用填，可自动获取'),
        null=True,
        blank=True,
        verbose_name=_('生产负责人'),
        limit_choices_to={'is_active': True},
        related_name='pack_batch_operator',
    )
    is_checked = models.BooleanField(_('已质检'), default=False)
    is_confirmed = models.BooleanField(_('数量已核验'), default=False)
    qualified = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('质检人员'),
        related_name='pack_batch_qualified',
        limit_choices_to={'is_active': True}
    )
    record_file = models.FileField(_('工单'), null=True, blank=True, upload_to='pack/%Y/%m/%d/')
    report = models.FileField(_('质检报告'), null=True, blank=True, upload_to='pack/%Y/%m/%d/')
    notes = models.TextField(_('备注'), max_length=1000, null=True, blank=True)
    file_is_upload = models.BooleanField(_('工单已上传'), default=False)
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='pack_batch_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='pack_batch_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('灌装批次')
        verbose_name_plural = _('灌装批次')

    def __str__(self):
        return "{0} {1}-剩余:{2}".format(
            self.code, self.product_package, self.left_amount
        )

    @mark_safe
    def materials(self):
        res = get_mos(filter_by={'related_order__pack_batch_id': self.pk})
        return res

    materials.short_description = "原料明细"
    operations = property(materials)


class ProcessRecord(models.Model):
    code = models.CharField(_('工单号'), max_length=100, null=True, blank=True)
    process_type = models.CharField(_('操作类型'), null=True, max_length=100, choices=choices_process_type)
    product_batch = models.ForeignKey(
        ProductBatch,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name=_('生产批次')
    )
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        help_text=_('不用填，可自动获取'),
        null=True,
        blank=True,
        verbose_name=_('操作负责人'),
        limit_choices_to={'is_active': True}
    )
    current_tank = models.ForeignKey(
        Equipment,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        limit_choices_to={'is_tank': True},
        verbose_name=_('转移至')
    )
    process_start = models.DateTimeField(_('操作开始'), null=True, blank=True)
    process_end = models.DateTimeField(_('操作结束'), null=True, blank=True)
    file = models.FileField(_('工单'), null=True, blank=True, upload_to='process/%Y/%m/%d/')
    file_is_upload = models.BooleanField(_('工单已上传'), default=False)
    notes = models.TextField(_('备注'), max_length=1000, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='process_record_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='process_record_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('操作记录')
        verbose_name_plural = _('操作记录')

    def __str__(self):
        process_type = self.process_type
        process_type_show = ''
        if process_type:
            for c in choices_process_type:
                if str(process_type) == c[0]:
                    process_type_show = c[1]
                    break
        return "{0} {1} {2} {3}".format(
            self.code,
            process_type_show,
            self.product_batch,
            self.process_start
        )

    @mark_safe
    def materials(self):
        res = get_mos(filter_by={'related_order__process_record_id': self.pk})
        return res

    materials.short_description = "原料明细"
    operations = property(materials)


class EquipmentUsageRecord(models.Model):
    equipment = models.ForeignKey(
        Equipment,
        on_delete=models.CASCADE,
        verbose_name=_('设备名称')
    )
    product_batch = models.ForeignKey(
        ProductBatch,
        on_delete=models.CASCADE,
        verbose_name=_('关联批次')
    )
    process_record = models.ForeignKey(
        ProcessRecord,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('关联操作记录')
    )
    datetime_start = models.DateTimeField(_('开始时间'), null=True, db_index=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-datetime_start']
        verbose_name = _('设备使用记录')
        verbose_name_plural = _('设备使用记录')

    def __str__(self):
        return "[{0}]-{1}".format(
            self.equipment,
            self.product_batch
        )


class EquipmentCurrentProductBatch(models.Model):
    equipment = models.OneToOneField(
        Equipment,
        on_delete=models.CASCADE,
        verbose_name=_('设备名称')
    )
    product_batch = models.ForeignKey(
        ProductBatch,
        on_delete=models.CASCADE,
        verbose_name=_('关联批次')
    )
    tank_state = models.CharField(
        _('发酵罐状态'),
        max_length=100,
        default='plan',
        choices=choices_tank_state
    )
    datetime_start = models.DateTimeField(_('开始时间'), null=True, db_index=True)

    objects = models.Manager()

    class Meta:
        ordering = ['equipment__code']
        verbose_name = _('设备当前批次')
        verbose_name_plural = _('设备当前批次')

    def __str__(self):
        return "[{0}]-{1}".format(
            self.equipment,
            self.product_batch
        )


class PackageGroup(models.Model):
    code = models.CharField(_('编号'), max_length=255, null=True, blank=True, unique=True)
    name = models.CharField(_('名称'), max_length=255, unique=True)
    reference_price = models.DecimalField(_('参考售价'), null=True, blank=True, decimal_places=2, max_digits=10)
    reference_cost = models.DecimalField(_('生产成本'), null=True, blank=True, decimal_places=2, max_digits=10)
    sales_cost = models.DecimalField(_('出厂成本'), null=True, blank=True, decimal_places=2, max_digits=10)
    has_cost = models.BooleanField(_('有生产成本'), default=False)
    has_sales_cost = models.BooleanField(_('有出厂成本'), default=False)
    total_amount = models.IntegerField(_('总数量'), default=1, null=True)
    length = models.IntegerField(_('长度/mm'), null=True, blank=True)
    width = models.IntegerField(_('宽度/mm'), null=True, blank=True)
    height = models.IntegerField(_('高度/mm'), null=True, blank=True)
    weight = models.DecimalField(_('毛重/kg'), null=True, blank=True, decimal_places=2, max_digits=10)
    weight_net = models.DecimalField(_('净含量/L'), null=True, blank=True, decimal_places=3, max_digits=10)
    barcode = models.CharField(_('条码'), max_length=100, null=True, blank=True, unique=True)
    safety_stock = models.IntegerField(_('安全库存'), null=True, default=0)
    current_stock = models.BigIntegerField(_('当前库存'), null=True, default=0)
    is_active = models.BooleanField(_('在用'), default=True)
    is_show = models.BooleanField(_('公开信息'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='package_group_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='package_group_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['code']
        verbose_name = _('包装组合')
        verbose_name_plural = _('包装组合')

    @mark_safe
    def packs(self):
        mis = PackageItem.objects.filter(package_group_id=self.pk).values(
            'product_package__code',
            'product_package__name',
            'product_package__barcode',
            'amount',
        )
        res = '<ul>'
        for mi in mis:
            res += '<li style="white-space: nowrap;">{0} {1}({2})*{3}</li>'.format(
                mi['product_package__code'],
                mi['product_package__name'],
                mi['product_package__barcode'],
                mi['amount'],
            )
        res += '</ul>'
        return res

    packs.short_description = "明细"
    operations = property(packs)

    def calculate_weight_net(self):
        weight_net = 0
        pis = PackageItem.objects.filter(
            package_group_id=self.pk
        ).values(
            'amount',
            'product_package__package_unit__amount',
            'product_package__package_unit__unit',
        )
        for pi in pis:
            if pi['product_package__package_unit__amount'] and pi['amount']:
                if pi['product_package__package_unit__unit'] == 'L':
                    weight_net += int(pi['amount']) * float(pi['product_package__package_unit__amount'])
                else:
                    weight_net += int(pi['amount']) * float(pi['product_package__package_unit__amount']) * 0.001
        return round(weight_net, 4)

    def __str__(self):
        return "[{0}]-{1}".format(
            self.code,
            self.name
        )


class SalesChannel(models.Model):
    name = models.CharField(_('渠道名称'), null=True, unique=True, max_length=255)
    sales_channel_type = models.SmallIntegerField(_('渠道类别'), choices=choices_sales_channel_type, db_index=True)
    is_public = models.BooleanField(_('是否公开'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='sales_channel_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='sales_channel_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['sales_channel_type', 'name']
        verbose_name = _('销售渠道')
        verbose_name_plural = _('销售渠道')

    def __str__(self):
        return "{0}-{1}".format(
            self.sales_channel_type, self.name
        )


class ProductSalesPrice(models.Model):
    package_group = models.ForeignKey(
        PackageGroup,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('关联产品')
    )
    sales_channel = models.ForeignKey(
        SalesChannel,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('渠道')
    )
    price = models.DecimalField(_('价格'), max_digits=10, decimal_places=2, null=True)
    notes = models.CharField(_('备注'), null=True, blank=True, max_length=255)
    is_active = models.BooleanField(_('是否有效'), default=True)
    is_public = models.BooleanField(_('是否公开'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='product_sales_price_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='product_sales_price_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('产品价格')
        verbose_name_plural = _('产品价格')

    def __str__(self):
        return "{0}-{1} {2}".format(
            self.sales_channel, self.package_group, self.price
        )


class PackageItem(models.Model):
    package_group = models.ForeignKey(
        PackageGroup,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('包装组合')
    )
    product_package = models.ForeignKey(
        ProductPackage,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('产品包装')
    )
    amount = models.IntegerField(_('数量'), default=1)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='package_item_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='package_item_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        unique_together = ['package_group', 'product_package']
        verbose_name = _('包装组合明细-产品')
        verbose_name_plural = _('包装组合明细-产品')

    def __str__(self):
        return "{0} {1}".format(
            self.product_package, self.amount
        )


class MaterialItem(models.Model):
    package_group = models.ForeignKey(
        PackageGroup,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('包装组合')
    )
    material = models.ForeignKey(
        Material,
        null=True,
        on_delete=models.PROTECT,
        limit_choices_to={'material_category__is_packing': True},
        verbose_name=_('包材')
    )
    amount = models.IntegerField(_('数量'), default=1)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_item_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_item_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        unique_together = ['package_group', 'material']
        verbose_name = _('包装组合明细-包材')
        verbose_name_plural = _('包装组合明细-包材')

    def __str__(self):
        return "{0} {1} {2}".format(
            self.package_group, self.material, self.amount
        )


class DocumentType(models.Model):
    name = models.CharField(_('名称'), max_length=255, unique=True, db_index=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['name']
        verbose_name = _('文件类型')
        verbose_name_plural = _('文件类型')

    def __str__(self):
        return "{0}".format(
            self.name
        )


class Document(models.Model):
    name = models.CharField(_('名称'), max_length=255)
    version = models.SmallIntegerField(_('版本'), null=True, default=1)
    file = models.FileField(_('文件'), null=True, blank=True, upload_to='docs/%Y/%m/%d/')
    document_type = models.ForeignKey(
        DocumentType,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name=_('文件类型')
    )
    related_department = models.ForeignKey(
        Department,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('部门')
    )
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('编写人员'),
        limit_choices_to={'is_active': True}
    )
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='document_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='document_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('文件列表')
        verbose_name_plural = _('文件列表')

    def __str__(self):
        return "{0} {1} v{2}".format(
            self.pk, self.name, self.version
        )

    @mark_safe
    def link(self):
        if self.file:
            return '<a href="{0}" target="_blank">文件</a>'.format(getattr(self, 'file').url)
        return ''

    link.short_description = _('链接')


class GroupBatch(models.Model):
    code = models.CharField(_('组合批次号'), max_length=100)
    package_group = models.ForeignKey(
        PackageGroup,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('包装组合')
    )
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        help_text=_('不用填，可自动获取'),
        null=True,
        blank=True,
        verbose_name=_('生产负责人'),
        limit_choices_to={'is_active': True},
    )
    amount = models.PositiveIntegerField(_('组合数量'), default=0)
    left_amount = models.IntegerField(_('剩余数量'), null=True, default=0)
    group_start = models.DateTimeField(_('组合开始'), null=True, blank=True)
    group_end = models.DateTimeField(_('组合结束'), null=True, blank=True)
    record_file = models.FileField(_('工单'), null=True, blank=True, upload_to='group/%Y/%m/%d/')
    is_active = models.BooleanField(_('在用'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='group_batch_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='group_batch_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('组合批次')
        verbose_name_plural = _('组合批次')

    def __str__(self):
        return "{0} {1} {2}".format(
            self.code,
            self.package_group.name,
            self.amount
        )

    @mark_safe
    def materials(self):
        res = get_mos(filter_by={'related_order__group_batch_id': self.pk})
        return res

    materials.short_description = "原料明细"
    operations = property(materials)


class MaterialOutOrder(models.Model):
    code = models.CharField(_('原材料出库单号'), null=True, blank=True, max_length=100, unique=True)
    record_date = models.DateField(_('出库日期'), null=True)
    record_datetime = models.DateTimeField(_('出库时间'), null=True, blank=True, help_text=_('此时间为虚拟计算值'))
    material_out_type = models.CharField(
        _('原料出库类别'),
        default='product',
        max_length=100,
        choices=choice_material_out_type
    )
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('出库人员'),
        limit_choices_to={'is_active': True}
    )
    product_batch = models.ForeignKey(
        ProductBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('生产批次'),
    )
    brew_batch = models.ForeignKey(
        BrewBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('糖化批次'),
    )
    pack_batch = models.ForeignKey(
        PackBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('灌装批次'),
    )
    process_record = models.ForeignKey(
        ProcessRecord,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('操作记录'),
    )
    group_batch = models.ForeignKey(
        GroupBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('组合批次'),
    )
    notes = models.TextField(_('备注'), max_length=2000, default='', blank=True, null=False)
    is_confirmed = models.BooleanField(_('已核验'), default=False)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_out_order_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_out_order_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('原材料出库单')
        verbose_name_plural = _('原材料出库单')

    def __str__(self):
        return "{0} {1}".format(
            self.code, self.record_date
        )

    @mark_safe
    def material_items_property(self):
        # 出库明细
        res = get_mos(filter_by={'related_order_id': self.pk})
        return res

    material_items_property.short_description = "出库明细"
    material_items = property(material_items_property)


class MaterialOut(models.Model):
    related_order = models.ForeignKey(
        MaterialOutOrder,
        on_delete=models.CASCADE,
        null=True,
        verbose_name=_('原材料出库单')
    )
    material_batch = models.ForeignKey(
        MaterialBatch,
        on_delete=models.PROTECT,
        null=True,
        verbose_name=_('原料批次'),
    )
    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        null=True,
        limit_choices_to={'is_active': True, 'warehouse_type': 'material'},
        verbose_name=_('仓库')
    )
    material = models.ForeignKey(
        Material,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name=_('原料'),
    )
    amount = models.DecimalField(_('数量'), max_digits=12, decimal_places=4, null=True)
    is_confirmed = models.BooleanField(_('已出库'), default=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='material_out_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='material_out_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True, db_index=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('原料出库')
        verbose_name_plural = _('原料出库')
        unique_together = ['related_order', 'material_batch']

    def __str__(self):
        return "{0} {1} {2} {3}".format(
            self.related_order.code if self.related_order else '',
            '({0} {1} {2})'.format(
                self.material_batch.production_date,
                self.material_batch.material.code,
                self.material_batch.material.name,
            ),
            self.amount,
            self.material_batch.unit if self.material_batch.unit else 'EA',
        )


class MaterialBatchStorage(models.Model):
    material_batch = models.ForeignKey(
        MaterialBatch,
        on_delete=models.CASCADE,
        verbose_name=_('原料批次'),
        related_name='material_batch_storage'
    )
    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        null=True,
        limit_choices_to={'is_active': True, 'warehouse_type': 'material'},
        verbose_name=_('仓库')
    )
    material_in = models.ForeignKey(
        MaterialIn,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('原料入库')
    )
    material_out = models.ForeignKey(
        MaterialOut,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('原料出库')
    )
    total_in = models.DecimalField(_('入库总量'), max_digits=12, decimal_places=4)
    left_amount = models.DecimalField(_('剩余数量'), max_digits=12, decimal_places=4)
    cw_total_in = models.DecimalField(_('仓库入库'), max_digits=12, decimal_places=4)
    cw_left_amount = models.DecimalField(_('仓库剩余'), max_digits=12, decimal_places=4)
    unit_price = models.DecimalField(_('采购单价（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit_cost = models.DecimalField(_('单位成本（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    record_datetime = models.DateTimeField(_('记录时间'), null=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-record_datetime']
        verbose_name = _('原料批次库存')
        verbose_name_plural = _('原料批次库存')

    def __str__(self):
        return "{0} {1} 剩余：{2} {3}".format(
            self.material_batch.material.code,
            self.material_batch.material.name,
            self.left_amount if self.left_amount else 0,
            self.material_batch.unit,
        )


class MaterialStorage(models.Model):
    material = models.ForeignKey(
        Material,
        on_delete=models.CASCADE,
        verbose_name=_('原料'),
        related_name='material_storage'
    )
    warehouse = models.ForeignKey(
        Warehouse,
        on_delete=models.PROTECT,
        limit_choices_to={'is_active': True, 'warehouse_type': 'material'},
        verbose_name=_('仓库')
    )
    material_batch_storage = models.ForeignKey(
        MaterialBatchStorage,
        on_delete=models.CASCADE,
        verbose_name=_('关联原料批次库存')
    )
    total_in = models.DecimalField(_('入库总量'), null=True, blank=True, max_digits=12, decimal_places=4)
    left_amount = models.DecimalField(_('剩余数量'), null=True, blank=True, max_digits=12, decimal_places=4)
    cw_total_in = models.DecimalField(_('仓库入库'), max_digits=12, decimal_places=4)
    cw_left_amount = models.DecimalField(_('仓库剩余'), max_digits=12, decimal_places=4)
    unit = models.CharField(_('计量单位'), max_length=10, null=True, blank=True, choices=choices_unit)
    unit_price = models.DecimalField(_('采购单价（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    unit_cost = models.DecimalField(_('单位成本（元）'), max_digits=12, decimal_places=4, null=True, blank=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('原料库存')
        verbose_name_plural = _('原料库存')

    def __str__(self):
        return "{0} {1} 剩余：{2} {3}".format(
            self.material.code,
            self.material.name,
            self.left_amount if self.left_amount else 0,
            self.unit if self.unit else 0,
        )


class PackBatchItem(models.Model):
    group_batch = models.ForeignKey(
        GroupBatch,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('组合批次'),
        related_name='pack_batch_item'
    )
    pack_batch = models.ForeignKey(
        PackBatch,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_('灌装批次'),
    )
    amount = models.PositiveIntegerField(_('数量'), default=0)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-id']
        verbose_name = _('组合-生产批次')
        verbose_name_plural = _('组合-生产批次')

    def __str__(self):
        return "{0}".format(
            self.pack_batch
        )


class FeedBackCategory(models.Model):
    name = models.CharField(_('问题归类'), max_length=100, null=True, unique=True)
    desc = models.TextField(_('描述'), max_length=1000, null=True, blank=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('问题归类')
        verbose_name_plural = _('问题归类')

    def __str__(self):
        return "{0}".format(
            self.name
        )


class FeedBack(models.Model):
    feedback_date = models.DateField(_('反馈日期'), null=True)
    feedback_type = models.CharField(_('反馈类型'), max_length=255, null=True, blank=True)
    feedback_category = models.ForeignKey(
        FeedBackCategory,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('问题归类')
    )
    level = models.SmallIntegerField(_('级别'), null=True, blank=True, default=1)
    channels = models.CharField(_('反馈渠道'), max_length=255, null=True, blank=True)
    related_customer = models.ForeignKey(
        Customer,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('关联客户')
    )
    content = models.TextField(_('反馈内容'), max_length=2000, null=True, blank=True)
    reply_by = models.ForeignKey(
        Staff,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('回复人员')
    )
    reply_content = models.TextField(_('回复内容'), max_length=2000, null=True, blank=True)
    process = models.TextField(_('处理方式'), max_length=2000, null=True, blank=True)
    cost = models.DecimalField(_('处理成本'), default=0, decimal_places=2, max_digits=10)
    reply_date = models.DateField(_('回复日期'), null=True, blank=True)
    product_name = models.ForeignKey(
        ProductName,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('产品名称')
    )
    product_package = models.ForeignKey(
        ProductPackage,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('产品包装')
    )
    package_group = models.ForeignKey(
        PackageGroup,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('包装组合')
    )
    amount = models.IntegerField(_('数量'), null=True, default=1)
    pack_batch = models.ForeignKey(
        PackBatch,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('灌装批次')
    )
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='feedback_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='feedback_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    @property
    def attachments(self):
        ats = FeedbackAttachment.objects.filter(related_feedback_id=self.pk)
        return ats

    class Meta:
        ordering = ['-feedback_date']
        verbose_name = _('客户反馈')
        verbose_name_plural = _('客户反馈')

    def __str__(self):
        return "{0} {1}".format(
            self.feedback_date, self.channels
        )


class FeedbackAttachment(models.Model):
    related_feedback = models.ForeignKey(
        FeedBack,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('关联的反馈')
    )
    file_name = models.CharField(_('附件名称'), max_length=255, null=True, blank=True)
    file = models.FileField(_('文件'), null=True, upload_to='feedback/%Y/%m/%d/')
    is_active = models.BooleanField(_('在用'), default=True)
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['id']
        verbose_name = _('反馈附件')
        verbose_name_plural = _('反馈附件')

    def __str__(self):
        return "{0}".format(
            self.file_name
        )

    def save(self, **kwargs):
        if not self.file_name:
            self.file_name = getattr(self, 'file').name
        super(FeedbackAttachment, self).save(**kwargs)


class MoneyInOutType(models.Model):
    name = models.CharField(_('名称'), max_length=255)
    is_negative = models.BooleanField(_('是支出'), default=False)

    objects = models.Manager()

    def __str__(self):
        return "{0} {1}".format(
            self.name, self.is_negative
        )

    class Meta:
        ordering = ['id']
        verbose_name = _('收支类型')
        verbose_name_plural = _('收支类型')


class MoneyInOut(models.Model):
    related_type = models.ForeignKey(
        MoneyInOutType,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_('收支类型')
    )
    payment_date = models.DateField(_('支付日期'), null=True)
    amount = models.DecimalField(_('金额'), max_digits=10, decimal_places=2, null=True)
    material_purchase_order = models.ForeignKey(
        MaterialPurchaseOrder,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('原料采购单')
    )
    operator = models.ForeignKey(
        Staff,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('操作员'),
        limit_choices_to={'is_active': True},
    )
    notes = models.CharField(_('备注'), max_length=255, null=True, blank=True)
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='money_in_out_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='money_in_out_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    def __str__(self):
        return "{0} {1}".format(
            self.payment_date, self.amount
        )

    class Meta:
        ordering = ['-id']
        verbose_name = _('收支记录')
        verbose_name_plural = _('收支记录')


class FermentMonitor(models.Model):
    product_batch = models.ForeignKey(
        ProductBatch,
        on_delete=models.CASCADE,
        verbose_name=_('酿造批次'),
        null=True
    )
    sg_plato = models.DecimalField(_('糖度或比重'), max_digits=5, decimal_places=3, null=True,
                                   blank=True, validators=[MaxValueValidator(50), MinValueValidator(0)])
    sg = models.DecimalField(_('比重'), max_digits=4, decimal_places=3, null=True,
                             blank=True, validators=[MaxValueValidator(1.2), MinValueValidator(1.0)])
    plato = models.DecimalField(_('糖度'), max_digits=3, decimal_places=1, null=True,
                                blank=True, validators=[MaxValueValidator(43), MinValueValidator(0)])
    ph = models.DecimalField(_('pH'), max_digits=4, decimal_places=2, null=True,
                             blank=True, validators=[MaxValueValidator(8), MinValueValidator(1)])
    t_real = models.DecimalField(_('真实温度'), max_digits=3, decimal_places=1, null=True,
                                 blank=True, validators=[MaxValueValidator(35), MinValueValidator(-5)])
    t_set = models.DecimalField(_('设置温度'), max_digits=3, decimal_places=1, null=True,
                                blank=True, validators=[MaxValueValidator(35), MinValueValidator(-5)])
    bar = models.DecimalField(_('压力(Bar)'), max_digits=3, decimal_places=2, null=True,
                              blank=True, validators=[MaxValueValidator(3), MinValueValidator(0)])
    diacetyl = models.DecimalField(
        _('双乙酰(mg/L)'), max_digits=4, decimal_places=3, null=True,
        blank=True, validators=[MaxValueValidator(2), MinValueValidator(0)])
    slag = models.DecimalField(_('排渣量(L)'), max_digits=4, decimal_places=2, null=True, blank=True)
    cell_mml = models.DecimalField(_('酵母数(10^6/ml)'), max_digits=6, decimal_places=2, null=True, blank=True)
    cell_pd = models.DecimalField(_('酵母死亡率%'), max_digits=6, decimal_places=2, null=True, blank=True)
    file = models.FileField(_('单据'), null=True, blank=True, upload_to='ferment')
    notes = models.TextField(_('备注'), max_length=1000, null=True, blank=True)
    recorded_at = models.DateTimeField(_('记录于'), null=True)
    recorded_by = models.ForeignKey(
        Staff,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('记录人员')
    )
    created_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='ferment_monitor_created_by',
    )
    updated_by = models.ForeignKey(
        WxUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='ferment_monitor_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    @property
    def delta_days(self):
        delta_days = None
        if self.recorded_at:
            delta_days = (getattr(self, 'recorded').date() - getattr(self.product_batch, 'date_start')).days
        return delta_days

    def __str__(self):
        return "{0} {1} {2}".format(
            self.product_batch,
            self.plato,
            self.ph
        )

    class Meta:
        ordering = ['-id']
        verbose_name = _('发酵监控')
        verbose_name_plural = _('发酵监控')
